package com.fivestar.pmanager.web.auth;

import com.fivestar.pmanager.model.FsUserBean;

import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.util.Assert;

import java.io.Serializable;
import java.util.*;

@SuppressWarnings("serial")
public class MyUserDetails implements UserDetails
{
    private final String username;
    private final Set<GrantedAuthority> authorities;
    private final boolean accountNonExpired;
    private final boolean accountNonLocked;
    private final boolean credentialsNonExpired;
    private final boolean enabled;
    private String password;
    private FsUserBean user;


    // ~ Constructors
    // ===================================================================================================
    public MyUserDetails(final String username, String password,
            final Collection<? extends GrantedAuthority> authorities,
            final boolean accountNonExpired, final boolean accountNonLocked,
            final boolean credentialsNonExpired, final boolean enabled,
            FsUserBean user)
    {
        if (username == null || "".equals(username) || password == null)
            throw new IllegalArgumentException(
                    "Cannot pass null or empty values to constructor");
        this.password = password;
        this.username = username;
        this.accountNonExpired = accountNonExpired;
        this.accountNonLocked = accountNonLocked;
        this.credentialsNonExpired = credentialsNonExpired;
        this.enabled = enabled;
        this.authorities = Collections
                .unmodifiableSet(sortAuthorities(authorities));
        this.user = user;
    }


    private static SortedSet<GrantedAuthority> sortAuthorities(
            Collection<? extends GrantedAuthority> authorities)
    {
        Assert.notNull(authorities,
                "Cannot pass a null GrantedAuthority collection");
        // Ensure array iteration order is predictable (as per
        // UserDetails.getAuthorities() contract and SEC-717)
        SortedSet<GrantedAuthority> sortedAuthorities = new TreeSet<GrantedAuthority>(
                new AuthorityComparator());
        for (GrantedAuthority grantedAuthority : authorities)
        {
            Assert.notNull(grantedAuthority,
                    "GrantedAuthority list cannot contain any null elements");
            sortedAuthorities.add(grantedAuthority);
        }
        return sortedAuthorities;
    }


    // ~ Methods
    // ========================================================================================================
    public Collection<GrantedAuthority> getAuthorities()
    {
        return authorities;
    }


    public String getPassword()
    {
        return password;
    }


    public String getUsername()
    {
        return username;
    }


    public boolean isAccountNonExpired()
    {
        return accountNonExpired;
    }


    public boolean isAccountNonLocked()
    {
        return accountNonLocked;
    }


    public boolean isCredentialsNonExpired()
    {
        return credentialsNonExpired;
    }


    public boolean isEnabled()
    {
        return enabled;
    }


    public void eraseCredentials()
    {
        password = null;
    }


    @Override
    public boolean equals(Object rhs)
    {
        if (rhs instanceof MyUserDetails)
            return username.equals(((MyUserDetails) rhs).username);
        return false;
    }


    @Override
    public int hashCode()
    {
        return username.hashCode();
    }


    @Override
    public String toString()
    {
        StringBuilder sb = new StringBuilder();
        sb.append(super.toString()).append(": ");
        sb.append("Username: ").append(this.username).append("; ");
        sb.append("Password: [PROTECTED]; ");
        sb.append("Enabled: ").append(this.enabled).append("; ");
        sb.append("AccountNonExpired: ").append(this.accountNonExpired)
                .append("; ");
        sb.append("credentialsNonExpired: ").append(this.credentialsNonExpired)
                .append("; ");
        sb.append("AccountNonLocked: ").append(this.accountNonLocked)
                .append("; ");
        if (!authorities.isEmpty())
        {
            sb.append("Granted Authorities: ");
            boolean first = true;
            for (GrantedAuthority auth : authorities)
            {
                if (!first)
                {
                    sb.append(",");
                }
                first = false;
                sb.append(auth);
            }
        }
        else
        {
            sb.append("Not granted any authorities");
        }
        return sb.toString();
    }


    public FsUserBean getUser()
    {
        return user;
    }


    public void setUser(FsUserBean user)
    {
        this.user = user;
    }

    private static class AuthorityComparator implements
            Comparator<GrantedAuthority>, Serializable
    {
        public int compare(GrantedAuthority g1, GrantedAuthority g2)
        {
            // Neither should ever be null as each entry is checked before
            // adding it to the set.
            // If the authority is null, it is a custom authority and should
            // precede others.
            if (g2.getAuthority() == null)
            {
                return -1;
            }
            if (g1.getAuthority() == null)
            {
                return 1;
            }
            return g1.getAuthority().compareTo(g2.getAuthority());
        }
    }

}
